"""
FIVEDASH(TM) -- General Purpose Accounting Software
Copyright (C) 2008  Obol Software Pty Ltd

This program is free software: you can redistribute it and/or modify
it. For terms and conditions, see fivedash/license/license.txt

"""

from mod_python import apache
from mod_python import Session
from mod_python import util
import psycopg2

from phunc.cx_wrapper import db_tools
from exception import GeneralBusinessErr
from error import genErrorPage
from logger import logger
from template import Template

__all__ = [
	'app_db_ip',
	'app_db_name',
	'app_db_user',
	'app_db_passwd',
	'check_session',
	'check_access',
	'get_connection',
	'get_app_connection',
	'get_template_dir',
	'user_is_root',
	'gen_site_map',
	'gen_menu',
	'Page',
	'info',
	'exception',
	'error',
	'block',
	'table',
	'form',
	'constants',
	'utils',
	'user_email',
	'DataBaseConnErr',
]

app_db_ip = 'localhost'
app_db_name = 'fivedash'
app_db_user = 'fivedash'
app_db_passwd = '{{password}}'

class DataBaseConnErr(Exception):
	pass
	
def check_session(req,session):
	if not session.has_key('login_user_name') and not session.has_key('login_user_id'):
		#raise GeneralBusinessErr('user_not_login','/bin/login.py/login_out')
		util.redirect(req,'/bin/root.py/access_forbit')

def check_access(req,session, url):
	"""
	Bugs out if the user does not have the required permissions.
	Returns True otherwise.
	"""
	if not session.has_key('login_user_name'):
		util.redirect(req,'/bin/root.py/access_forbit')
	#access_list = session['user_access'].split(',')
	access_list = session['user_access'].values()
	if url=='/' or url.rfind('index') > 0:
		return True
	else:
		for access in access_list:
			logger.debug("%s	%s" % (url, "/"+access+"/") )
			if url.rfind( "/" + access  ) > 0:
				return True
	util.redirect(req,'/bin/root.py/access_forbit')

def get_connection(session):
	"""
	Connects to a 'managed company' database.
	Returns a connection object.
	"""
	for item in ['ip', 'name','user','passwd']:
		if not session.has_key('company_db_'+item) or len(session['company_db_'+item]) == 0:
			raise GeneralBusinessErr('can not connect to company database!')
	logger.debug('<connect>')
	cx = psycopg2.connect('host=%s dbname=%s user=%s password=%s' % (session['company_db_ip'], session['company_db_name'], app_db_user,app_db_passwd))
	logger.debug('</connect>')
	return cx
		
def get_app_connection(host=app_db_ip,dbname=app_db_name,user=app_db_user,passwd=app_db_passwd):
	"""
	Connects to app database.
	Returns a connection object.
	"""
	try:
		conn=psycopg2.connect('host=%s dbname=%s user=%s password=%s' % (host,dbname,user,passwd))
		return conn
	except:
		raise DataBaseConnErr
	
def get_template_dir(req):		
	"""
	Locates the base directory for page templates.
	Returns a string.
	"""
	return req.document_root()[:req.document_root().rindex('skins')]+ 'templates/'

def user_is_root(session, cursor):
	"""
	Checks for root privileges.
	Returns a boolean.
	"""
	return db_tools.exec_action(cursor, 'user_is_root', [int(session['login_user_id'])])[0]['is_root']

def gen_site_map(is_root_page=True, is_root_user=False, company_name=None, person_name=None, ledger=None):
	"""
	Selects/creates the relevant 'site structure' info - mostly for breadcrumbs.
	Returns a dictionary.
	"""
	if is_root_page:
		return {
			'root':[is_root_user and 'Root Menu' or 'Global Menu','loginpage'],
			'users':['Users','user_management'],
			'groups':['Groups','group_management'],
			'grants':['Grants','grant_right_page'],
			'companies':['Managed Companies','company_management'],
			'myprefs':['My Prefs','mypage']
		}
	else:
		return {
			'operations':['Operations','index'],
			'invoices':['Invoices','show_invoice'],
			'porders':['Purchase Orders','show_purchase_order'],
		
			'finance':['Finance','index'],
			'journals':['Journals','list_journal'],
			'subledger':[ledger + ' Ledger','sub_account_index'],
			'subjournals':['Journals','list_sub_journal'],
		
			'crm':['CRM','index'],
			'person':[person_name,'get_contact_detail'],
		
			'admin':['Admin','index'],

			'extras':['Extras','index'],
				
			'root':['My Prefs','mypage'],
				
		}

def gen_menu(is_root_page, url, query_string=None, sl=None):
	"""
	Selects/creates the relevant menu labels and links.
	Returns a dictionary.
	"""
	if url.rfind('mybin') > 0:
		from extras_lib import menu
		menu['operations'] = ['Operations','/bin/operations.py']
		menu['finance'] = ['Finance','/bin/finance.py']
		menu['crm'] = ['CRM','/bin/crm.py']
		menu['admin'] = ['Admin','/bin/admin.py']
		menu['extras'] = ['Extras','/mybin/extras.py']
		return menu
	elif is_root_page:
		return {
			'users':['Manage Users','/bin/root.py/user_management'],
			'groups':['Manage Groups','/bin/root.py/group_management'],
			'grants':['Grant User Rights','/bin/root.py/grant_right_page'],
			'companies':['Modify Company Details','/bin/root.py/company_management'],
			'passwd':['Change Password','/bin/root.py/change_password_page'],
			'skin':['Change Skin','/bin/root.py/change_skin_page'],
			'portal':['Change Default Portal','/bin/root.py/change_default_portal_page']
		}
	else:
		return {
			'operations':['Operations','/bin/operations.py'],
			'invoice':['Create Invoice', '/bin/operations.py/insert_invoice' + get_query_string(query_string)],
			'receipt':['Issue Receipt', '/bin/operations.py/payment_list_page' + get_query_string(query_string)],
			'billpayment':['Bill Payment',  '/bin/operations.py/bill_payment_page' + get_query_string(query_string)],
			'porder':['Create Purchase Order', '/bin/operations.py/insert_purchase_order' + get_query_string(query_string)],
			'newcustomer':['New Customer', '/bin/operations.py/create_customer_entity_type' + get_query_string(query_string)],
			'newsupplier':['New Supplier', '/bin/operations.py/create_supplier_entity_type' + get_query_string(query_string)],
		
			'finance':['Finance','/bin/finance.py'],
			'recordexp':['Record Expenses', '/bin/finance.py/insert_expenses' + get_query_string(query_string)],
			'account':['Create an Account', '/bin/finance.py/insert_account' + get_query_string(query_string)],
			'special':['Set Special Accounts','/bin/finance.py/special_params_setting_page' + get_query_string(query_string)],
			'linked':['Set Tax Accounts','/bin/finance.py/linked_account_setting_page' + get_query_string(query_string)],
			'subledger':['Create a Subsidiary Ledger', '/bin/finance.py/insert_sub_ledger' + get_query_string(query_string)],
			'journal':['Write a Journal', '/bin/finance.py/insert_journal' + get_query_string(query_string)],
			'editjournal':['Edit or Delete Journals', '/bin/finance.py/list_journal' + get_query_string(query_string)],
			'postjournal':['Post Journals', '/bin/finance.py/list_post_journal' + get_query_string(query_string)],
			'balsheet':['Balance Sheet', '/bin/report/create_report.py?report_id=1' + query_string.replace('?', '&amp;')],
			'pl':['Profit &amp; Loss Report', '/bin/report/create_report.py?report_id=2' + query_string.replace('?', '&amp;')],
			'taxreport':['Tax Report', '/bin/report/create_report.py?report_id=3' + query_string.replace('?', '&amp;')],
			'subaccount':['Create an Account', '/bin/finance.py/insert_sub_account1?sl=' + sl + query_string.replace('?', '&amp;')],
			'subjournal':['Write a Journal', '/bin/finance.py/insert_sub_journal?sl=' + sl + query_string.replace('?', '&amp;')],
			'editsubjournal':['Edit or Delete Journals', '/bin/finance.py/list_sub_journal?sl=' + sl + query_string.replace('?', '&amp;')],
			'postsubjournal':['Post Journals', '/bin/finance.py/list_sub_post_journal?sl=' + sl + query_string.replace('?', '&amp;')],
		
			'crm':['CRM','/bin/crm.py'],
			'newcontact':['New Contact', '/bin/crm.py/create_contact' + get_query_string(query_string)],
		
			'admin':['Admin','/bin/admin.py'],
			'newuser':['New User','/bin/admin.py/create_user_page' + get_query_string(query_string)],
			'newsalesagent':['New Sales Agent', '/bin/admin.py/create_sales_agent_page' + get_query_string(query_string)],
			'newtax':['New Transaction Tax', '/bin/admin.py/insert_transaction_tax'],
			'smtpset':['Configure SMTP Settings','/bin/admin.py/smtp_server_setting' + get_query_string(query_string)],

			'extras':['Extras','/mybin/extras.py'],
				
			'passwd':['Change Password','/bin/root.py/change_password_page'],
			'skin':['Change Skin','/bin/root.py/change_skin_page'],
			'portal':['Change Default Portal','/bin/root.py/change_default_portal_page']
		}
def get_query_string(query_string):
		if query_string.find('&amp;')==0:
				return query_string[:5].replace('&amp;','?')+query_string[5:]
		elif query_string.find('&')==0:
				return query_string[:1].replace('&','?')+query_string[1:]
		else:
				return query_string		
storedSession = {}
lastAccessed = None
def setSessionTimeout(session):		
		global storedSession
		global lastAccessed
		if lastAccessed:
			if lastAccessed-session.created()>=info.RESET_TIME_LIMIT:
				for key,value in session.iteritems():
					storedSession[key] = value
		else:
			for key,value in session.iteritems():
				storedSession[key] = value
		if session.is_new():
			if lastAccessed:
				if session.created()-lastAccessed<info.session_timeout:
					for key,value in storedSession.iteritems():
						session[key] = value
					session.save()
					session.set_timeout(info.session_timeout-session.created()+lastAccessed)
		#eval lastAccessed
		lastAccessed = session.last_accessed()
class Page:
	"""
	This is the main class of the application.

	Each page in the application (with the exception of the login form) is an instance
	of this class. Access control, merging of templates with associated values from the
	instantiating script to generate output html, and basic error handling all happen here.

	Connections are created (if required) that can be shared by the instantiating script.
	This is the preferred method of getting a database connection.

	The is_root_page parameter of __init__ informs as to whether the page lives
	in the 'Root Menu' area, or within the portals area of a managed company's system
	(there are significant differences between the page structures of those two areas).
	Note that is_root_page=True overrides app_db=False and creates the connection anyway.
	"""
	def __init__(self, req, app_db=False, company_db=False, is_root_page=False, superuser_only=False, no_menu=False):
		"""Check access, create connections, initialise page dictionaries, reset session timeout"""		
		#try:
		self.session = Session.Session(req,timeout=info.session_timeout)
		#self.session.load()
		setSessionTimeout(self.session)
		if is_root_page:
			check_session(req,self.session)
		else:
			check_access(req,self.session, req.uri)
		if app_db or is_root_page: self.app_db = get_app_connection()
		if superuser_only and not user_is_root(self.session, self.app_db.cursor()):
			raise GeneralBusinessErr('access_forbit','/bin/login.py/login_out')
		if company_db: self.company_db = get_connection(self.session)
		self.req = req
		self.is_root_page = is_root_page
		self.no_menu = no_menu
		self.info = {}
		self.setup = {}
		self.content = {}
		#except GeneralBusinessErr, e:
			#return genErrorPage(e.getErrMsg(), e.getURL())
	def __setitem__(self,name,value):
		if name=='is_root_page':
			self.is_root_page=value
	def __getitem__(self,name):
		if name=='is_root_page':
			return self.is_root_page
	def render(self, debug=False):
		"""Apply templates & defaults, generate page"""
		if debug:
			# drop the error handling
			# -- this is a copy of the try block below
			# -- unfriendly formatting for the sake of brevity
			template = Template()
			template.parseFile((self.req.uri.rfind('mybin') > 0 and '/var/fivedash/custom/templates/' or get_template_dir(self.req)) + self.setup['template'])
			page_content = template.render(self.content)
			if self.no_menu: return page_content
			setup_defaults = {'window_title':'Fivedash','breadcrumbs':['Undefined area'],'menu_title':'Admin Process','menu':['spacer'],'content_title':'','broadcast_message':None,'form':False,'javascript':False,}
			info_defaults = {'session':self.session,'query_string':'','ledger':'','sl':'','a':None,'name':None,'id':None,'j':'','v':'x.x','t':'x.x','query_params':{},'group':None}
			for key, value in setup_defaults.iteritems(): self.setup[key] = self.setup.has_key(key) and self.setup[key] or value
			for key, value in info_defaults.iteritems(): self.info[key] = self.info.has_key(key) and self.info[key] or value
			# js file
			if self.setup['javascript']:
				import os					
				js_file_name=os.path.splitext(os.path.split(self.setup['template'])[1])[0]+'.js'					
				template.parseFile(os.path.join(get_template_dir(self.req)[:get_template_dir(self.req).rindex('templates')] ,'js',js_file_name))
				js_content = template.render(self.content)
				js_file_path = os.path.join(info.JS_TEMP_PATH,js_file_name)
				utils.save_upload_file(info.JS_TEMP_PATH, js_file_path, js_content)
				self.setup['js_file']=js_file_name
			# js file end
			self.setup['breadcrumb_links'] = self.setup['breadcrumbs'][:-1]
			query_params_str = None
			if self.info['query_params']:
				query_params_str=''
				for key,value in self.info['query_params'].items():
					query_params_str += '%s=%s&amp;'%(str(key),str(value))
				query_params_str=query_params_str[:-5]
			if self.is_root_page: site_map = gen_site_map(is_root_user=user_is_root(self.session, self.app_db.cursor()))
			else: site_map = gen_site_map(False, False, self.session['company_name'], self.info['name'], self.info['ledger'])
			menu_items = gen_menu(self.is_root_page, self.req.uri, self.info['query_string'], self.info['sl'])
			self.session.save()
			#self.session['user_access'] = (self.session.has_key('user_access') and self.session['user_access']) and self.session['user_access'].split(',') or None
			self.session['user_access'] = self.session.has_key('user_access') and self.session['user_access'] or None
			content = {'page_setup':self.setup,'page_info':self.info,'page_content':page_content,'session':self.session,'site_map':site_map,'menu_items':menu_items,'query_params_str':query_params_str}
			template = Template()
			template.parseFile(get_template_dir(self.req) + (self.is_root_page and 'html_root.opt' or 'html.opt'))
			html = template.render(content)
			return html
		else:
			try:
				# apply template
				template = Template()
				template.parseFile(
					(self.req.uri.rfind('mybin') > 0 and '/var/fivedash/custom/templates/' or get_template_dir(self.req)) +
					self.setup['template']
				)
				page_content = template.render(self.content)
				if self.no_menu: return page_content

				# apply defaults
				setup_defaults = {
					'window_title':'Fivedash',
					'breadcrumbs':['Undefined area'],
					'menu_title':'Admin Process',
					'menu':['spacer'],
					'content_title':'',
					'broadcast_message':None,
					'form':False,
					'javascript':False
				}
				info_defaults = {
					'session':self.session,
					'query_string':'',
					'ledger':'',
					'sl':'',
					'a':None,
					'name':None,
					'id':None,
					'j':'',
					'v':'x.x',
					't':'x.x',
					'query_params':{},
					'group':None
				}
				for key, value in setup_defaults.iteritems():
					self.setup[key] = self.setup.has_key(key) and self.setup[key] or value		
				for key, value in info_defaults.iteritems():
					self.info[key] = self.info.has_key(key) and self.info[key] or value

				# create javascript file if necessary - this is for XHMTL 1.1 compliance
				if self.setup['javascript']:
					import os					
					js_file_name=os.path.splitext(os.path.split(self.setup['template'])[1])[0]+'.js'					
					template.parseFile(os.path.join(get_template_dir(self.req)[:get_template_dir(self.req).rindex('templates')] ,'js',js_file_name))
					js_content = template.render(self.content)
					js_file_path = os.path.join(info.JS_TEMP_PATH,js_file_name)
					utils.save_upload_file(info.JS_TEMP_PATH,js_file_path, js_content)
					self.setup['js_file']=js_file_name

				# pander to the renderer's inability to slice lists
				self.setup['breadcrumb_links'] = self.setup['breadcrumbs'][:-1]

				# generate query string if necessary
				query_params_str = None
				if self.info['query_params']:
					query_params_str=''
					for key,value in self.info['query_params'].items():
						query_params_str += '%s=%s&amp;'%(str(key),str(value))
					query_params_str=query_params_str[:-5]

				# generate site map and menu items
				if self.is_root_page:
					site_map = gen_site_map(is_root_user=user_is_root(self.session, self.app_db.cursor()))
				else:
					site_map = gen_site_map(False, False, self.session['company_name'], self.info['name'], self.info['ledger'])
				menu_items = gen_menu(self.is_root_page, self.req.uri, self.info['query_string'], self.info['sl'])

				# reset session timeout and prepare user_access to be spoon fed to the renderer as a list
				self.session.save()
				#self.session['user_access'] = (self.session.has_key('user_access') and self.session['user_access']) and self.session['user_access'].split(',') or None
				self.session['user_access'] = self.session.has_key('user_access') and self.session['user_access'] or None

				# put it all together
				content = {
					'page_setup':self.setup,
					'page_info':self.info,
					'page_content':page_content,
					'session':self.session,
					'site_map':site_map,
					'menu_items':menu_items,
					'query_params_str':query_params_str
				}

				# invoke renderer
				template = Template()
				logger.debug("****************************************")
				logger.debug(self.is_root_page)
				template.parseFile(get_template_dir(self.req) + (self.is_root_page and 'html_root.opt' or 'html.opt'))
				html = template.render(content)

				return html

			except GeneralBusinessErr, e:
				return genErrorPage(e.getErrMsg(), e.getURL())
			except Exception, e:
				errmsg = '%s'.replace('<','').replace('>','').replace('.__str__','') % e.__str__ + ': ' + ','.join([str(arg) for arg in e.args])
				return genErrorPage(errmsg, self.req.uri)
			except:
				return genErrorPage(errurl=self.req.uri)
